#ifndef PILO_STRINGMANIPLATIONS_H
#define PILO_STRINGMANIPLATIONS_H

#include "common/common.h"
#include "string/stringoutput.h"
#include "memory/pilotblockallocator.h"
#include "memory/cheapallocator.h"

namespace PILO
{ 
    namespace Core
    {
        namespace String {

#           if defined(WIN32) || defined(WINDOWS)

#           else
            size_t _compat_mbslen(const unsigned char * mbstring);
            size_t _compat_mbstowcs(wchar_t *dest, const char *src, size_t n);
#           endif


            BOOL IsCharWhiteSpace(char c);
            BOOL IsCharWhiteSpace(wchar_t wc);

            void* MemoryMove(void* dst, const void* src, size_t count);

            TriState IsMutibyteString(const char * str, size_t len = MC_INVALID_SIZE);

            size_t StringLength(const char * strSource);
            size_t StringLength(const wchar_t * strSource);
            size_t StringLength(const unsigned char * strSource);

            INT32 StringCompare(const char * str1,          const char * str2,          size_t length=MC_INVALID_SIZE);
            INT32 StringCompare(const unsigned char * str1, const unsigned char * str2, size_t length=MC_INVALID_SIZE);
            INT32 StringCompare(const wchar_t * str1,       const wchar_t * str2,       size_t length=MC_INVALID_SIZE);
            INT32 StringCaseIgnoredCompare(const char * str1,          const char * str2,          size_t length=MC_INVALID_SIZE);
            INT32 StringCaseIgnoredCompare(const unsigned char * str1, const unsigned char * str2, size_t length=MC_INVALID_SIZE);
            INT32 StringCaseIgnoredCompare(const wchar_t * str1,       const wchar_t * str2,       size_t length=MC_INVALID_SIZE);



            //template functions
            template <typename T>
            const T* StringFind(const T* strSource, const T* needle, size_t count = MC_INVALID_SIZE)
            {
                if (strSource == MC_NULLPTR_OF_(T*)) {
                    return MC_NULLPTR_OF_(T*);
                }

                //return source string is needle is zero length (or null)
                if (needle == MC_NULLPTR_OF_(T*)) {
                    return strSource;
                }
                size_t needleLength = StringLength(needle);
                if (needleLength <=0) {
                    return strSource;
                }

                size_t strSourceLength;
                if (count == MC_INVALID_SIZE) {
                    strSourceLength = StringLength(strSource);
                    if (strSourceLength <= 0) {
                        return MC_NULLPTR_OF_(T*);
                    } 
                } else {
                    strSourceLength = count;
                }

                if (strSourceLength <= 0) {
                    return strSource;
                }
                
                for (size_t i=0; i<strSourceLength; i++) {
                    size_t charsRemains = strSourceLength - i;
                    if (needleLength > charsRemains) {
                        return MC_NULLPTR_OF_(T*);
                    }
                    if (0 == StringCompare(strSource+i,needle, needleLength)) {
                        return (strSource+i);
                    }
                }
                
                return MC_NULLPTR_OF_(T*);
            }


            template <typename T>
            T* StringFind(T* strSource, const T* needle, size_t count = MC_INVALID_SIZE)
            {
                if (strSource == MC_NULLPTR_OF_(T*)){
                    return MC_NULLPTR_OF_(T*);
                }

                //return source string is needle is zero length (or null)
                if (needle == MC_NULLPTR_OF_(T*)){
                    return strSource;
                }
                size_t needleLength = StringLength(needle);
                if (needleLength <=0) {
                    return strSource;
                }

                size_t strSourceLength;
                if (count == MC_INVALID_SIZE) {
                    strSourceLength = StringLength(strSource);
                    if (strSourceLength <= 0) {
                        return MC_NULLPTR_OF_(T*);
                    } 
                } else {
                    strSourceLength = count;
                }

                if (strSourceLength <= 0) {
                    return strSource;
                }

                for (size_t i=0; i<strSourceLength; i++) {
                    size_t charsRemains = strSourceLength - i;
                    if (needleLength > charsRemains) {
                        return MC_NULLPTR_OF_(T*);
                    }
                    if (0 == StringCompare(strSource+i,needle, needleLength)) {
                        return (strSource+i);
                    }
                }

                return MC_NULLPTR_OF_(T*);
            }

            template <typename T>
            const T* StringCaseIgnoredFind(const T* strSource, const T* needle, size_t count = 0)
            {
                if (strSource == MC_NULLPTR_OF_(T*)){
                    return MC_NULLPTR_OF_(T*);
                }

                //return source string is needle is zero length (or null)
                if (needle == MC_NULLPTR_OF_(T*)){
                    return strSource;
                }
                size_t needleLength = StringLength(needle);
                if (needleLength <=0) {
                    return strSource;
                }

                size_t strSourceLength;
                if (count == MC_INVALID_SIZE) {
                    strSourceLength = StringLength(strSource);
                    if (strSourceLength <= 0) {
                        return MC_NULLPTR_OF_(T*);
                    } 
                } else {
                    strSourceLength = count;
                }

                if (strSourceLength <= 0) {
                    return strSource;
                }

                for (size_t i=0; i<strSourceLength; i++) {
                    size_t charsRemains = strSourceLength - i;
                    if (needleLength > charsRemains) {
                        return MC_NULLPTR_OF_(T*);
                    }
                    if (0 == StringCaseIgnoredCompare(strSource+i,needle, needleLength)) {
                        return (strSource+i);
                    }
                }

                return MC_NULLPTR_OF_(T*);
            }


            template <typename T>
            T* StringCaseIgnoredFind(T* strSource, const T* needle, size_t count = 0)
            {
                if (strSource == MC_NULLPTR_OF_(T*)){
                    return MC_NULLPTR_OF_(T*);
                }

                //return source string is needle is zero length (or null)
                if (needle == MC_NULLPTR_OF_(T*)){
                    return strSource;
                }
                size_t needleLength = StringLength(needle);
                if (needleLength <=0) {
                    return strSource;
                }

                size_t strSourceLength;
                if (count == MC_INVALID_SIZE) {
                    strSourceLength = StringLength(strSource);
                    if (strSourceLength <= 0) {
                        return MC_NULLPTR_OF_(T*);
                    } 
                } else {
                    strSourceLength = count;
                }

                if (strSourceLength <= 0) {
                    return strSource;
                }

                for (size_t i=0; i<strSourceLength; i++) {
                    size_t charsRemains = strSourceLength - i;
                    if (needleLength > charsRemains) {
                        return MC_NULLPTR_OF_(T*);
                    }
                    if (0 == StringCaseIgnoredCompare(strSource+i,needle, needleLength)) {
                        return (strSource+i);
                    }
                }

                return MC_NULLPTR_OF_(T*);
            }


            // template <typename T>
            // T* PILOTStringAllocSubstitute(const T* strSource, size_t count, const T* needle1, const T* needle2, PILO::Core::Memory::PILOIMemoryAllocator * memoryAllocator == NULL)
            // {
            //     if ((strSource == MC_NULL_POINTER(T))){
            //         return MC_NULL_POINTER(T);
            //     }
            //    
            //     T*  var;
            //     T*  tmp_pos;
            //     T*  needle_pos;
            //     T*   count;
            //     size_t   len;
            // 
            //     /* Calculate the size of the new string
            //         */
            //     len = PILOStringLength(strSource);
            // 
            //     if (PILOStringLength (needle1) != PILOStringLength(needle2)) {
            //         count   = 0;
            //         tmp_pos = (T*)strSource;
            // 
            //         while ((needle_pos = (char*) strstr(tmp_pos, needle1))) {
            //                         tmp_pos = needle_pos + strlen (needle1);
            //                         count++;
            //                 }
            // 
            //                 len += strlen(str) + (strlen(needle2) - strlen(needle1)) * count;
            //         }
            // 
            // 
            // 
            // }
            // 


            template <typename T>
            T * StringAllocCopy(const T * strSource, size_t count, PILO::Core::Memory::PILOIMemoryAllocator * memoryAllocator)
            {
                if ((strSource == MC_NULLPTR_OF_(T*))){
                    return MC_NULLPTR_OF_(T*);
                }

                size_t srcLen;
                if (count <=0 ){
                    srcLen = StringLength(strSource);
                } else {
                    srcLen = count;
                }        
                size_t dstByteLen = (srcLen + 1) * sizeof(T);
                T* dest;
                if (memoryAllocator == 0) {
                    PILO::Core::Memory::CHeapAllocator allocator;
                    dest = static_cast<T*>(allocator.alloc(dstByteLen));
                } else {
                    dest = static_cast<T*>(memoryAllocator->alloc(dstByteLen));
                }

                if (dest == MC_NULLPTR_OF_(T*)) {
                    return MC_NULLPTR_OF_(T*);
                }

                T* retString = StringCopy((T*)dest, (srcLen + 1), strSource, srcLen);
                
                return retString;
            }

            template <typename T>
            T* StringCopy(T * strDest, size_t destCount, const T * strSource, size_t count = MC_INVALID_SIZE)
            {
                if ((strDest == MC_NULLPTR_OF_(T*)) || (strSource == MC_NULLPTR_OF_(T*))){
                    return strDest;
                }

                T* strRet = strDest;
                size_t numCopy = 0;

                size_t maxCopyCount ;
                if ((destCount == MC_INVALID_SIZE) && (count == MC_INVALID_SIZE)) {
                    maxCopyCount = MC_INVALID_SIZE;
                } else if (destCount == MC_INVALID_SIZE) {
                    maxCopyCount = count;
                } else if (count == MC_INVALID_SIZE) {
                    maxCopyCount = destCount - 1;
                } else {
                    maxCopyCount = ((destCount - 1) < count) ? (destCount - 1) : count;
                }

                if (maxCopyCount == MC_INVALID_SIZE) {
                    while ((*strDest++ = *strSource++));

                } else {
                    while (numCopy < maxCopyCount) {
                        if (0 == (*strSource)){
                            break;
                        } 
                        *strDest = *strSource;
                        numCopy++;
                        strDest++;
                        strSource++;
                    }
                    *strDest = 0;
                }
               
                return strRet;
            }

            template <typename T>
            size_t StringAppendString(T * appendee, size_t appendeeLength, size_t appendeeCapacity, const T* appender, size_t length = MC_INVALID_SIZE)
            {
                if (0 == appendeeCapacity) {
                    return 0;
                }

	            //validate appendee and length param
	            if(MC_NULLPTR_OF_(T*) == appendee) {
		            return MC_INVALID_SIZE;
	            }
	            //validate appendeeLength param
	            if(appendeeLength == MC_INVALID_SIZE) {//need autocalculate
		            appendeeLength = StringLength(appendee);
	            }

	            //validate appender
	            if(MC_NULLPTR_OF_(T*) == appender){
		            return appendeeLength;
	            }
	            if (length == MC_INVALID_SIZE) {
		            length = StringLength(appender);
	            }
	            if (length == 0) {
		            return appendeeLength; //means noop
	            }
            	
	            size_t adderCount = 0;
	            while(0 != *appender) {
		            appendee[appendeeLength] = *appender;
		            appendeeLength ++;
		            appender ++;
		            adderCount ++;

		            if (appendeeCapacity != MC_INVALID_SIZE) {
			            if (appendeeLength >= appendeeCapacity-1) {
				            break;
			            }
		            }
		            if (length > 0) {
			            if (adderCount >= length) {
				            break;
			            }
		            }
            		
	            }
	            appendee[appendeeLength] = 0;

	            return appendeeLength;
            }

            template <typename T1, typename T2>
            size_t StringAppendFormatted(T1 * appendee, size_t appendeeLength, size_t capacity, const T1 * fmt, T2 value)
            {
	            T1 tmpBuffer[64];
	            size_t ret = FormatOutputToString(tmpBuffer, MCF_COUNT_OF(tmpBuffer), fmt, value);
	            if (ret != MC_INVALID_SIZE) {
		            return StringAppendString(appendee,appendeeLength, capacity, tmpBuffer, MC_INVALID_SIZE);
	            }

	            return MC_INVALID_SIZE;
            }

            template <typename T>
            size_t StringTrimLeft(T* s, size_t length=MC_INVALID_SIZE)
            {
	            if (s == MC_NULLPTR_OF_(T*)) {
		            return MC_INVALID_SIZE;
	            }
	            if (length == MC_INVALID_SIZE) {
		            length = StringLength(s);
	            }
	            if (length == 0) {
		            return 0;
	            }
	            size_t i = 0;
	            while ((i < length) && (IsCharWhiteSpace(s[i]))) {
		            i++;
	            }
	            if (i == 0) {//no white space
		            return length;
	            }
	            if (i == length) { //all white space
		            *s = 0;
		            return 0;
	            }
	            size_t spaceLen = (length - i);
	            T *pSrc = &s[i];

	            MemoryMove((void*)s, (const void*)pSrc, spaceLen * sizeof(T));
	            s[length - i] = 0;

	            return (length-i);
            }

            template <typename T>
            size_t StringTrimRight(T * s, size_t length=MC_INVALID_SIZE)
            {
	            if (s == MC_NULLPTR_OF_(T*)) {
		            return MC_INVALID_SIZE;
	            }
	            if (length == MC_INVALID_SIZE) {
		            length = StringLength(s);
	            }
	            if (length == 0) {
		            return 0;
	            }

	            size_t i = length - 1;
	            while(i >= 0 && IsCharWhiteSpace(s[i])) {
		            i -= 1;
	            }
	            if (i < 0) {
		            *s = 0;
		            length = 0;
	            } else if(i != length - 1) {
		            length = i + 1;
		            s[length] = 0;
	            }

	            return length;
            }

            template <typename T>
            size_t StringTrim(T* s, size_t length=MC_INVALID_SIZE)
            {
	            size_t lenAfterTrim = StringTrimRight(s,length);
	            if (lenAfterTrim != MC_INVALID_SIZE) {
		            return StringTrimLeft(s, lenAfterTrim);
	            }
                return MC_INVALID_SIZE;
            }
        } /* String */ 
    }
} /* PILO */


#endif /* PILO_STRINGMANIPLATIONS_H */
